package common.liusany.admin.service.security;

import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class FormAuthenticationWithLockFilter extends FormAuthenticationFilter {

	private long maxLoginAttempts = 2;

	public static ConcurrentHashMap<String, AtomicLong> accountLockMap = new ConcurrentHashMap<String, AtomicLong>();

	private static final Logger log = LoggerFactory.getLogger(FormAuthenticationWithLockFilter.class);
	
	@Override
	protected boolean onAccessDenied(ServletRequest request,
			ServletResponse response) throws Exception {
		System.out.println("onAccessDenied run.................");
		 if (isLoginRequest(request, response)) {
	            if (isLoginSubmission(request, response)) {
	                System.out.println("post 请求登录提交检测，尝试登录");
	                return executeLogin(request, response);
	            } else {
	                System.out.println("GET或者其他方式的请求    返回到登录页面");
	                return true;
	            }
	            
	        } else {
	            System.out.println("Attempting to access a path which requires authentication.  Forwarding to the " +
                        "Authentication url [" + getLoginUrl() + "]");
	            
	            redirectToLogin(request, response);
//	            saveRequestAndRedirectToLogin(request, response);
	            return false;
	        }
	}
	
	
	
	
	
	
	

	@Override
	protected boolean executeLogin(ServletRequest request,
			ServletResponse response) throws Exception {
		
		System.out.println("executeLogin");
		AuthenticationToken token = createToken(request, response);

		if (token == null) {
			String msg = "createToken method implementation returned null. A valid non-null AuthenticationToken "
					+ "must be created in order to execute a login attempt.";
			throw new IllegalStateException(msg);
		}

		if (checkIfAccountLocked(request)) {
			return onLoginFailure(token, new ExcessiveAttemptsException(),
					request, response);
		} else {
			if (!doLogin(request, response, token)) {
				resetAccountLock(getUsername(request));
				return false;
			}
			return true;
		}
	}

	private boolean checkIfAccountLocked(ServletRequest request) {
		
		
		System.out.println("checkIfAccountLocked");
		
		String username = getUsername(request);
		if (accountLockMap.get((String) username) != null) {
			long remainLoginAttempts = accountLockMap.get((String) username)
					.get();
			if (remainLoginAttempts <= 0) {
				return true;
			}
		}
		return false;
	}

	private boolean doLogin(ServletRequest request, ServletResponse response,
			AuthenticationToken token) throws Exception {
		
		
		System.out.println("doLogin");
		
		try {
			Subject subject = getSubject(request, response);
			subject.login(token);

			return onLoginSuccess(token, subject, request, response);
		} catch (IncorrectCredentialsException e) {
			decreaseAccountLoginAttempts(request);
			checkIfAccountLocked(request);

			return onLoginFailure(token, e, request, response);
		} catch (AuthenticationException e) {
			return onLoginFailure(token, e, request, response);
		}
	}

	private void decreaseAccountLoginAttempts(ServletRequest request) {
		
		System.out.println("减少一次登录次数");
		AtomicLong initValue = new AtomicLong(maxLoginAttempts);
		AtomicLong remainLoginAttempts = accountLockMap.putIfAbsent(
				getUsername(request), new AtomicLong(maxLoginAttempts));
		if (remainLoginAttempts == null) {
			remainLoginAttempts = initValue;
		}
		remainLoginAttempts.getAndDecrement();
		accountLockMap.put(getUsername(request), remainLoginAttempts);
	}

	private void resetAccountLock(String username) {
		accountLockMap.put(username, new AtomicLong(maxLoginAttempts));
	}

	public void setMaxLoginAttempts(long maxLoginAttempts) {
		this.maxLoginAttempts = maxLoginAttempts;
	}
}
